package com.yanyeori.framework.core.thread;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.ttl.threadpool.TtlExecutors;
import com.yanyeori.framework.core.constant.BaseConstant;
import com.yanyeori.framework.core.util.SpringUtil;
import com.yanyeori.framework.core.util.ThreadUtil;
import org.springframework.lang.Nullable;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 异步任务管理器
 */
public class AsyncManager {

    private AsyncManager() {
    }

    private static volatile AsyncManager INSTANCE;

    public static AsyncManager get() {
        if (INSTANCE == null) {
            synchronized (AsyncManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new AsyncManager();
                }
            }
        }
        return INSTANCE;
    }

    private static final int OPERATE_DELAY_TIME = 10;

    private final ExecutorService TTL_EXECUTOR_SERVICE = SpringUtil.getBean(BaseConstant.TTL_EXECUTOR_SERVICE);
    private final ScheduledExecutorService TTL_SCHEDULED_EXECUTOR_SERVICE = SpringUtil.getBean(BaseConstant.TTL_SCHEDULED_EXECUTOR_SERVICE);

    public static ExecutorService getTtlExecutorService() {
        return get().TTL_EXECUTOR_SERVICE;
    }

    public static ScheduledExecutorService getTtlScheduledExecutorService() {
        return get().TTL_SCHEDULED_EXECUTOR_SERVICE;
    }

    public static void execute(Runnable task) {
        getTtlExecutorService().execute(task);
    }

    public static <T> Future<T> submit(Callable<T> task) {
        return getTtlExecutorService().submit(task);
    }

    public static <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
        try {
            return getTtlExecutorService().invokeAll(tasks);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static ScheduledFuture<?> schedule(Runnable task) {
        return schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
    }

    public static ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit) {
        return getTtlScheduledExecutorService().schedule(task, delay, unit);
    }

    public static void shutdown() {
        ThreadUtil.shutdownAndAwaitTermination(getTtlExecutorService());
        ThreadUtil.shutdownAndAwaitTermination(getTtlScheduledExecutorService());
    }

    public static void concurrentExecute(List<Runnable> tasks, int parallel, @Nullable ExecutorService executor) {
        if (CollUtil.isEmpty(tasks)) return;
        ExecutorService executorService = executor == null ? getTtlExecutorService() : TtlExecutors.getTtlExecutorService(executor);
        int parallelNum = Math.min(tasks.size(), parallel);
        AtomicInteger count = new AtomicInteger(0);
        for (int i = 0; i < parallelNum; i++) {
            execute(tasks, count, executorService);
        }
    }

    private static void execute(List<Runnable> tasks, AtomicInteger count, ExecutorService executorService) {
        if (count.get() == tasks.size()) return;
        executorService.execute(() -> {
            tasks.get(count.getAndIncrement()).run();
            execute(tasks, count, executorService);
        });
    }
}
